﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Diagnostics;
using GenericLibrary.ExtentionMethods;
using DiscreteEventModel;
using DiscreteEventModel.Common;

namespace SpikingNeuralNetwork.Model {

    public abstract partial class NeuralNetwork {

        private ObjectBase Backstore { get; set; }
        private readonly Dictionary<StreamBase.Token, NeuralAgent> ActiveAgents;
        private readonly ConcurrentDictionary<StreamBase.Token, NeuralAgent> InactiveAgents;

        protected NeuralNetwork(string filePath, int dataBlockSize, int dataBlockCount, double backtrackingTime) {
            this.Backstore = new ObjectBase(filePath, dataBlockSize, dataBlockCount);
            this.ActiveAgents = new Dictionary<StreamBase.Token, NeuralAgent>();
            this.InactiveAgents = new ConcurrentDictionary<StreamBase.Token, NeuralAgent>();
        }

        public bool TryGetAgent<TAgent>(StreamBase.Token id, out TAgent agent)
            where TAgent : NeuralAgent, new() {
            NeuralAgent baseAgent;
            if(this.ActiveAgents.TryGetValue(id, out baseAgent)) {
                agent = baseAgent as TAgent;
                if(agent == null) {
                    throw new Exception();
                }
                return true;
            } else if(this.InactiveAgents.TryRemove(id, out baseAgent)) {
                this.ActiveAgents[id] = baseAgent;
                agent = baseAgent as TAgent;
                if(agent == null) {
                    throw new Exception();
                }
                return true;
            } else if(this.Backstore.TryGet<TAgent>(id, out agent)) {
                this.ActiveAgents[id] = agent;
                return true;
            }
            return false;
        }

        public bool TryGetAgent<TAgent, TFactory>(StreamBase.Token id, out TAgent agent)
            where TAgent : NeuralAgent
            where TFactory : Singleton<TFactory>, IFactory<TFactory, TAgent>, new() {
            NeuralAgent baseAgent;
            if(this.ActiveAgents.TryGetValue(id, out baseAgent)) {
                agent = baseAgent as TAgent;
                if(agent == null) {
                    throw new Exception();
                }
                return true;
            } else if(this.InactiveAgents.TryRemove(id, out baseAgent)) {
                this.ActiveAgents[id] = baseAgent;
                agent = baseAgent as TAgent;
                if(agent == null) {
                    throw new Exception();
                }
                return true;
            } else if(this.Backstore.TryGet<TAgent, TFactory>(id, out agent)) {
                this.ActiveAgents[id] = agent;
                return true;
            }
            return false;
        }

        private void Dump() {
            Task.Run(() => {
                NeuralAgent[] inactiveAgents = new NeuralAgent[this.InactiveAgents.Count];
                this.InactiveAgents.Values.CopyTo(inactiveAgents, 0);
                for(int i = 0; i < inactiveAgents.Length; i++) {
                    NeuralAgent inactiveAgent = inactiveAgents[i];
                    if(!this.Backstore.TryUpdate(inactiveAgent.ID, inactiveAgent) || !this.InactiveAgents.TryRemove(inactiveAgent.ID, out inactiveAgent)) {
                        throw new Exception();
                    }
                }
            });
        }
    }
}
